package libcore.view

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
import android.view.ViewConfiguration
import android.widget.Scroller
import androidx.core.graphics.toColorInt
import org.jaaksi.kottlindemo.R
import kotlin.math.abs
import kotlin.math.ceil
import kotlin.math.roundToInt
import kotlin.properties.Delegates

/**
 * 刻度尺刻度尺控件
 * 1.只支持设置进制，如scale=10，即1个大刻度=10个小刻度
 * 2.支持水平、竖直方向
 * 3.支持正向、反向刻度
 * 4.刻度线支持圆角
 * 5.支持设置刻度线颜色、长度、宽度、间距等
 * 6.刻度值支持设置字体
 * 7.支持切换单位（切换数据）
 * 8.支持自定义刻度值格式化输出
 */
class RulerView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    companion object {
        const val HORIZONTAL = 0
        const val VERTICAL = 1
    }

    private var isForward = false // 指针和刻度文字是否正向（水平：向下未正，竖直：向右为正）
    private var orientation = HORIZONTAL
    private var isHorizontal by Delegates.notNull<Boolean>()// 是否横向
    var selectedValue = 100f // 未选择时 默认的值 滑动后表示当前中间指针正在指着的值
        private set
    private var maxValue = 200f // 最大数值
    private var minValue = 0f //最小的数值

    private var maxOptionalValue = maxValue
    private var minOptionalValue = minValue

    var targetValue = -1f // 目标值，默认无

    // 目标值与当前值区域颜色
    private var highlightColor = "#33354AD8".toColorInt()
        set(value) {
            field = value
            highlightPaint.color = value
            invalidate()
        }

    // 最小单位  如 1:表示 每2条刻度差为1
    private var perValue by Delegates.notNull<Float>()

    // 进制，默认为10进制。英尺、英寸是12进制
    private var scale: Int = 10
    private var scaleValueFormatter: ScaleValueFormatter? = null
    private val formatter: ScaleValueFormatter
        get() {
            return if (scaleValueFormatter != null) {
                scaleValueFormatter!!
            } else {
                DefaultScaleValueFormatter()
            }
        }

    private var lineCornerRadius = 0f // 线条圆角半径，默认为0无圆角
    private var lineSpace = 18f //  尺子刻度2条线之间的距离
    private var lineWidth = 6f //  尺子刻度的宽度
    private var lineHeight = 48f //  lineHeight  表示最短的那个高度(也就是 1 2 3 4 等时的高度)
    private var midLineHeight = 60f //  mLineMidHeight  表示中间的高度(也就是 5  15 25 等时的高度)
    private var maxLineHeight = 90f //  尺子刻度分为3中不同的高度。 mLineMaxHeight表示最长的那根(也就是 10的倍数时的高度)

    private var lineColor = Color.GRAY //刻度的颜色
    private var maxLineColor = Color.GRAY //长刻度的颜色，默认与普通刻度颜色一致
    private var textColor = Color.BLACK //文字的颜色

    private var textMargin = 30f // 刻度值文本距离刻度线的间距
    private var textSize = 30f //尺子刻度下方数字 textsize
    private var alphaEnable = false // 尺子 最左边 最后边是否需要透明 (透明效果更好点)

    private var lineCount = 0 //共有多少条 刻度

    private var baselineOffset = 0f
    private var selectedOffset = 0f // 当前选中的刻度距离最小刻度的偏移量（像素）
//    private var maxOffset = 0f //所有刻度 共有多长
    private var maxOptionalOffset = 0f // 最大可选值偏移量
    private var minOptionalOffset = 0f // 最小可选值偏移量


    // 最小值整数偏移量
    private var startOffset by Delegates.notNull<Int>()

    private var lastPos = 0
    private var move = 0
    private var onValueChangedListener: OnValueChangedListener? = null // 刻度回调

    //Scroller是一个专门用于处理滚动效果的工具类   用mScroller记录/计算View滚动的位置，再重写View的computeScroll()，完成实际的滚动
    private var scroller = Scroller(context)
    private var minVelocity = 0 // 最小滑动距离

    //主要用跟踪触摸屏事件（flinging事件和其他gestures手势事件）的速率。
    private var velocityTracker = VelocityTracker.obtain()
    private var textPaint = Paint(Paint.ANTI_ALIAS_FLAG) // 尺子刻度下方数字( 也就是每隔10个出现的数值) paint
    private var linePaint = Paint(Paint.ANTI_ALIAS_FLAG) //  尺子刻度  paint
    private var highlightPaint = Paint(Paint.ANTI_ALIAS_FLAG) //  高亮 paint

    init {
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.RulerView)
        alphaEnable = typedArray.getBoolean(R.styleable.RulerView_rv_alphaEnable, alphaEnable)
        isForward = typedArray.getBoolean(R.styleable.RulerView_rv_isForward, isForward)
        orientation = typedArray.getInteger(R.styleable.RulerView_rv_orientation, orientation)
        isHorizontal = orientation == HORIZONTAL
        lineCornerRadius =
            typedArray.getDimension(R.styleable.RulerView_rv_lineCornerRadius, lineCornerRadius)
        lineWidth = typedArray.getDimension(R.styleable.RulerView_rv_lineWidth, lineWidth)
        // 这里的计算规则没有考虑刻度线的宽度，所以这里要加上刻度线宽度才是真实的刻度线之间的距离
        lineSpace =
            typedArray.getDimension(R.styleable.RulerView_rv_lineSpace, lineSpace) + lineWidth
        maxLineHeight =
            typedArray.getDimension(R.styleable.RulerView_rv_maxLineHeight, maxLineHeight)
        midLineHeight =
            typedArray.getDimension(R.styleable.RulerView_rv_midLineHeight, midLineHeight)
        lineHeight =
            typedArray.getDimension(R.styleable.RulerView_rv_lineHeight, lineHeight)
        lineColor = typedArray.getColor(R.styleable.RulerView_rv_lineColor, lineColor)
        maxLineColor = typedArray.getColor(R.styleable.RulerView_rv_maxLineColor, maxLineColor)
        textSize = typedArray.getDimension(R.styleable.RulerView_rv_textSize, textSize)
        textColor = typedArray.getColor(R.styleable.RulerView_rv_textColor, textColor)
        textMargin =
            typedArray.getDimension(R.styleable.RulerView_rv_textMargin, textMargin)
        selectedValue = typedArray.getFloat(R.styleable.RulerView_rv_selectedValue, selectedValue)
        minValue = typedArray.getFloat(R.styleable.RulerView_rv_minValue, minValue)
        maxValue = typedArray.getFloat(R.styleable.RulerView_rv_maxValue, maxValue)
        minOptionalValue = minValue
        maxOptionalValue = maxValue
        scale = typedArray.getInt(R.styleable.RulerView_rv_scale, scale)
        perValue = typedArray.getFloat(R.styleable.RulerView_rv_perValue, 1f) * scale
        minVelocity = ViewConfiguration.get(getContext()).scaledMinimumFlingVelocity
        typedArray.recycle()

        calculateData()
        initPaint()
    }

    private fun initPaint() {
        textPaint.textSize = textSize
        textPaint.color = textColor
        linePaint.strokeWidth = lineWidth
        highlightPaint.color = highlightColor

        // Align只会改变水平方向绘制点，竖直方向无作用
        if (isHorizontal) {
            textPaint.textAlign = Paint.Align.CENTER
        } else {
            if (isForward) {
                textPaint.textAlign = Paint.Align.LEFT
            } else {
                textPaint.textAlign = Paint.Align.RIGHT
            }
        }
        initFontBaselineOffset()
    }

    // 策略文本BaseLine
    private fun initFontBaselineOffset() {
        val fontMetrics = textPaint.fontMetrics
        baselineOffset = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom
    }

    private fun calculateData() {
        lineCount = ((this.maxValue * scale - this.minValue * scale) / perValue).toInt() + 1
//        maxOffset = ((lineCount - 1) * lineSpace)
        // 获取最小最大可选值偏移量
        minOptionalOffset = (this.minOptionalValue - this.minValue) / perValue * lineSpace * scale
        maxOptionalOffset = (this.maxOptionalValue - this.minValue) / perValue * lineSpace * scale
        // bugfixe 解决startOffset计算错误
        // 这里* 10是为了解决float运算问题
        startOffset = ((minValue * 10) % (perValue * 10)).toInt()
        if (startOffset >= scale) {
            startOffset /= scale
        }
        calculateOffset()
    }

    private fun calculateOffset() {
        selectedOffset = (this.selectedValue - this.minValue) / perValue * lineSpace * scale
    }

    fun setOnValueChangeListener(listener: OnValueChangedListener?) {
        onValueChangedListener = listener
    }

    /**
     * 设置刻度值自定义格式化输出
     */
    fun setScaleValueFormatter(formatter: ScaleValueFormatter) {
        this.scaleValueFormatter = formatter
    }

    /**
     * 设置刻度值数字的字体
     */
    fun setTextTypeFace(typeFace: Typeface) {
        textPaint.typeface = typeFace
        initFontBaselineOffset()
        invalidate()
    }

    fun setValue(selectedValue: Float) {
        this.selectedValue = when {
            selectedValue < minValue -> minValue
            selectedValue > maxValue -> maxValue
            else -> selectedValue
        }
        if (!scroller.isFinished) {
            scroller.forceFinished(true)
        }
        calculateOffset()
        notifyValueChanged()
        invalidate()
    }

    /**
     * @param selectedValue 未选择时 默认的值 滑动后表示当前中间指针正在指着的值
     * @param minValue      最大数值
     * @param maxValue      最小的数值
     * @param per           最小单位  如 1:表示 每2条刻度差为1
     * @param scale         进制  如 10:表示 1大刻度=10小刻度
     * @param minOptionalValue 最小可选值
     * @param maxOptionalValue 最大可选值
     */
    fun setValue(
        selectedValue: Float,
        minValue: Float,
        maxValue: Float,
        per: Float,
        scale: Int,
        minOptionalValue: Float = minValue,
        maxOptionalValue: Float = maxValue
    ) {
        this.selectedValue = when {
            selectedValue < minOptionalValue -> minOptionalValue
            selectedValue > maxOptionalValue -> maxOptionalValue
            else -> selectedValue
        }
        if (!scroller.isFinished) {
            scroller.forceFinished(true)
        }

        this.maxValue = maxValue
        this.minValue = minValue
        this.minOptionalValue = minOptionalValue
        this.maxOptionalValue = maxOptionalValue
        perValue = per * scale
        this.scale = scale
        calculateData()
        notifyValueChanged()
        invalidate()
//        visibility = VISIBLE
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        // 水平方向计算wrap高度，竖直方向计算wrap宽度
        val tHeight =
            if (isHorizontal && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
                ceil(maxLineHeight + textMargin + 2 * baselineOffset).toInt()
            } else {
                MeasureSpec.getSize(heightMeasureSpec)
            }

        val tWidth =
            if (!isHorizontal && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
                ceil(maxLineHeight + textMargin + textPaint.measureText(maxValue.toString())).toInt()
            } else {
                MeasureSpec.getSize(widthMeasureSpec)
            }
        setMeasuredDimension(tWidth, tHeight)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (isHorizontal) {
            drawHorizontal(canvas)
        } else {
            drawVertical(canvas)
        }
    }

    private fun drawVertical(canvas: Canvas) {
        // 起始纵坐标
        var start: Float
        // 刻度尺长度
        var scaleLength: Float
        var value: String
        var alpha = 0
        var scale: Float
        // 中间刻度尺坐标
        val midPoint = height / 2
        for (i in 0 until lineCount) {
            // 向上为-，向下未正，但是刻度是反过来的，最下面的是最小刻度，最上面是最大刻度
            start = midPoint + selectedOffset - i * lineSpace
            if (start < 0 || start > height) {
                continue  //  先画默认值在正中间，左右各一半的view。  多余部分暂时不画(也就是从默认值在中间，画旁边左右的刻度线)
            }
            scaleLength = when {
                (i + startOffset) % this.scale == 0 -> {
                    linePaint.color = maxLineColor
                    maxLineHeight
                }
                (i + startOffset) % (this.scale / 2) == 0 -> {
                    linePaint.color = lineColor
                    midLineHeight
                }
                else -> {
                    linePaint.color = lineColor
                    lineHeight
                }
            }
            if (alphaEnable) {
                scale = 1 - abs(start - midPoint) / midPoint
                alpha = (255 * scale * scale).toInt()
                linePaint.alpha = alpha
            }

            // 绘制刻度线
            if (isForward) {
                canvas.drawRoundRect(
                    0f,
                    start - lineWidth / 2,
                    scaleLength,
                    start + lineWidth / 2,
                    lineCornerRadius,
                    lineCornerRadius,
                    linePaint
                )
            } else {
                canvas.drawRoundRect(
                    width - scaleLength,
                    start - lineWidth / 2,
                    width.toFloat(),
                    start + lineWidth / 2,
                    lineCornerRadius,
                    lineCornerRadius,
                    linePaint
                )
            }

            if ((i + startOffset) % this.scale == 0) {
                value = formatter.format(minValue + i * perValue / this.scale)
                if (alphaEnable) {
                    textPaint.alpha = alpha
                }
                if (isForward) {
                    canvas.drawText(
                        value,
                        scaleLength + textMargin,
                        start + baselineOffset,
                        textPaint
                    )
                } else {
                    canvas.drawText(
                        value,
                        width - scaleLength - textMargin,
                        start + baselineOffset,
                        textPaint
                    )
                }

            }
        }

    }


    private fun drawHorizontal(canvas: Canvas) {
        // 刻度线起始坐标
        var start: Float
        // 刻度线长度
        var scaleLength: Float
        var value: String
        var alpha = 0
        var scale: Float
        // 中间刻度线坐标
        val midPoint = width / 2
        // todo 这里可以根据mOffset计算出在范围内的起始位置，避免不必要的for循环
        for (i in 0 until lineCount) {
            start = midPoint - selectedOffset + i * lineSpace
            if (start < 0 || start > width) {
                continue  //  先画默认值在正中间，左右各一半的view。  多余部分暂时不画(也就是从默认值在中间，画旁边左右的刻度线)
            }
            scaleLength = when {
                (i + startOffset) % this.scale == 0 -> {
                    linePaint.color = maxLineColor
                    maxLineHeight
                }
                i + startOffset % (this.scale / 2) == 0 -> {
                    linePaint.color = lineColor
                    midLineHeight
                }
                else -> {
                    linePaint.color = lineColor
                    lineHeight
                }
            }
            if (alphaEnable) {
                scale = 1 - abs(start - midPoint) / midPoint
                alpha = (255 * scale * scale).toInt()
                linePaint.alpha = alpha
            }

            // 绘制刻度线
            if (isForward) {
                canvas.drawRoundRect(
                    start - lineWidth / 2,
                    scaleLength,
                    start + lineWidth / 2,
                    0f,
                    lineCornerRadius, lineCornerRadius,
                    linePaint
                )
            } else {
                canvas.drawRoundRect(
                    start - lineWidth / 2,
                    height.toFloat() - scaleLength,
                    start + lineWidth / 2,
                    height.toFloat(),
                    lineCornerRadius, lineCornerRadius,
                    linePaint
                )
            }

            // 绘制文本
            if ((i + startOffset) % this.scale == 0) {
                value = formatter.format((minValue + i * perValue / this.scale))
                if (alphaEnable) {
                    textPaint.alpha = alpha
                }

                if (isForward) {
                    canvas.drawText(
                        value,
                        start,
                        scaleLength + textMargin + baselineOffset * 2,
                        textPaint
                    )
                } else {
                    canvas.drawText(value, start, height - scaleLength - textMargin, textPaint)
                }

            }
        }

        if (targetValue >= 0) {
            val select = minValue + selectedOffset / lineSpace * perValue / this.scale
            if (targetValue != select) {
                val targetPoint = midPoint - (select - targetValue) * 10 / perValue * lineSpace
                canvas.drawRect(
                    midPoint.toFloat(),
                    height.toFloat() - maxLineHeight,
                    targetPoint,
                    height.toFloat(),
                    highlightPaint
                )
            }
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        val action = event.action
        val currPos = if (isHorizontal) event.x.toInt() else event.y.toInt()
        velocityTracker.addMovement(event)
        when (action) {
            MotionEvent.ACTION_DOWN -> {
                scroller.forceFinished(true)
                lastPos = currPos
                move = 0
            }
            MotionEvent.ACTION_MOVE -> {
                move = if (isHorizontal) {
                    lastPos - currPos
                } else {
                    currPos - lastPos
                }

                changeMoveAndValue()
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                countMoveEnd()
                countVelocityTracker()
                return false
            }
        }
        lastPos = currPos
        return true
    }

    private fun countVelocityTracker() {
        velocityTracker.computeCurrentVelocity(500) //初始化速率的单位
        if (isHorizontal) {
            val xVelocity = velocityTracker.xVelocity //当前的速度
            if (abs(xVelocity) > minVelocity) {
                scroller.fling(0, 0, xVelocity.toInt(), 0, Int.MIN_VALUE, Int.MAX_VALUE, 0, 0)
            }
        } else {
            val yVelocity = velocityTracker.yVelocity //当前的速度
            if (abs(yVelocity) > minVelocity) {
                scroller.fling(0, 0, 0, yVelocity.toInt(), 0, 0, Int.MIN_VALUE, Int.MAX_VALUE)
            }
        }

    }

    /**
     * 滑动结束后，若是指针在2条刻度之间时，改变mOffset 让指针正好在刻度上。
     */
    private fun countMoveEnd() {
        selectedOffset += move.toFloat()
        if (selectedOffset <= minOptionalOffset) {
            selectedOffset = minOptionalOffset
        } else if (selectedOffset >= maxOptionalOffset) {
            selectedOffset = maxOptionalOffset
        }
        lastPos = 0
        move = 0
        selectedValue =
            minValue + (selectedOffset / lineSpace).roundToInt() * perValue / scale
        selectedOffset = (selectedValue - minValue) * scale / perValue * lineSpace
        notifyValueChanged()
        postInvalidate()
    }

    /**
     * 滑动后的操作
     */
    private fun changeMoveAndValue() {
        selectedOffset += move.toFloat()
        if (selectedOffset >= maxOptionalOffset) {
            selectedOffset = maxOptionalOffset
            move = 0
            scroller.forceFinished(true)
        } else if (selectedOffset <= minOptionalOffset) {
            selectedOffset = minOptionalOffset
            move = 0
            scroller.forceFinished(true)
        }
        selectedValue =
            minValue + (selectedOffset / lineSpace).roundToInt() * perValue / scale
        selectedOffset = (selectedValue - minValue) * scale / perValue * lineSpace
        notifyValueChanged()
        postInvalidate()
    }

    private fun notifyValueChanged() {
        if (null != onValueChangedListener) {
            onValueChangedListener!!.onValueChanged(selectedValue)
        }
    }

    /**
     * 滑动后的回调
     */
    interface OnValueChangedListener {
        fun onValueChanged(value: Float)
    }

    override fun computeScroll() {
        super.computeScroll()
        //mScroller.computeScrollOffset()返回 true表示滑动还没有结束
        if (scroller.computeScrollOffset()) {
            if (isHorizontal) {
                if (scroller.currX == scroller.finalX) {
                    countMoveEnd()
                } else {
                    val xPosition = scroller.currX
                    move = lastPos - xPosition
                    changeMoveAndValue()
                    lastPos = xPosition
                }
            } else {
                if (scroller.currY == scroller.finalY) {
                    countMoveEnd()
                } else {
                    val yPosition = -scroller.currY
                    move = lastPos - yPosition
                    changeMoveAndValue()
                    lastPos = yPosition
                }
            }
        }
    }

    private inner class DefaultScaleValueFormatter : ScaleValueFormatter {
        override fun format(value: Float): String {
            return value.toInt().toString()
        }
    }

    interface ScaleValueFormatter {
        /**
         * @param value minValue + 偏移格数 * per
         */
        fun format(value: Float): String
    }

}